Appendix C : Emulator File Formats

This section describes all the common file formats in the field of Commodore 64 emulation. It is intended for the interested programmer, who might wish to learn the internal workings of the emulator. If you are only interested in playing your favorite C64 game, then you do not need to worry about these details.

C.1 *.P00 Program Format

The *.P00 format is a flexible file format, that is able to support all common types of C64 files. As it has a magic constant at the head of the file it also enables the emulator to make sure that no invalid files are used. This makes it the recommended file format for single files.

typedef struct
 
  Byte P00Magic[8];    /* $00 - $07 */
  Byte OrigFName[17];  /* $08 - $18 */
  Byte RecordSize;     /* $19 */
  Byte Data[n];        /* $1A - ... */
  P00File;

P00Magic   - Magic constant 'C64File' =
                              $43, $36, $34, $46, $69, $6C, $65, $00
OrigFName  - Original C64 filename (CBM Charset)
RecordSize - Record size for RELative files
Data       - The actual data

This file format was first used by Wolfgang Lorenz in PC64. Note that the extension *.P00 is not fix. rather the 'P' stands for 'PRG' and can become an 'S' for 'SEQ', or 'R' for 'REL'. Furthermore the '00' can be used count to '01','02','03'... to resolve name conflicts caused by truncation a 16 character C64 filename to a 8+3 MS-DOS name. Of course this is not relevant on a Macintosh, it's just a hint if you should ever happen to run across a *.P01 file.

C.2 *.C64 Program Format

The *.C64 format is the exact format that the original Commodore 64 uses to store a programs to either disk or tape.

typedef struct
 
  Byte LoadAddrLow, LoadAddrHigh; /* $00 - $01 */
  Byte PrgData[n];                /* $02 - ... */
  C64File;

LoadAddr - The address in C64 RAM at which the program will get stored when loaded with secondary device number 1.
PrgData - The actual program.

This file format was introduced to the world of emulators by Miha Peternel in C64S. As can be seen from the above description this is a rather simple file format. Still it's use is not recommended as it does not provide the emulator with any possibility to make sure that only valid files are used.

C.3 *.X64 Floppy Disk Image Format 

The *.X64 format is flexible format for disk image files that supports a wide range of floppy disks. Note so, that the implementation of *.X64 in Power64 can only handle 1541 disks.

typedef struct
 
  Byte X64Magic[4];   /* $00 - $03 */
  Byte Version[2];    /* $04 - $05 */
  Byte DiskType;      /* $06 */
  Byte TrackCnt;      /* $07 */
  Byte SecondSide;    /* $08 */
  Byte ErrorFlag;     /* $09 */
  Byte Reserved[22];  /* $0A - $1F */
  Byte DiskInfo[31];  /* $20 - $3E */
  Byte ConstZero;     /* $3F */
  Byte DiskImage[683 * 256];
  X64File;

X64Magic   - Magic constant 'C'1541/64 = $43, $15, $41, $64
Version    - C1541 Version 2.6 = $02, $06
DiskType   - Floppy disk type: 1541 = $01
             Other defined values: (not usable for Power64)
               0..1540, 1..1541, 2..1542, 3..1551,
        4..1570, 5..1571, 6..1572, 8..1581,
       16..2031&4031, 17..2040&3040, 18..2041
       24..4040,
       32..8050, 33..8060, 34..8061,
       48..SFD 1001, 49..8250, 50..8280,
TrackCnt   - Number of Tracks on the Disk (Side 0) = 35
SecondSide - Is it a Double Sided Disks (0..No, 1..Yes) = $00
ErrorFlag  - Flag for Error (precise meaning unclear) (unused)
Reserved   - Must be $00
DiskInfo   - Description of the Disk Image (in ASCII or ISO Latin/1)
ConstZero  - Must be $00
DiskImage  - 683 disk sectors of 256 bytes each.
             (For more information on DiskImage see also: *.D64)

The *.X64 disk image format was originally created by Teemu Rantanen for use in X64.
As *.X64 uses a well defined header the emulator is able to make sure that only valid files are used. This is a clear advantage over the (unfortunately more widespread) *.D64 format.

C.4 *.D64 Floppy Disk Image Format 

The *.D64 file format is a 1:1 copy of all sectors as they appear on a floppy disk. On a C1541 formatted disk (and thus also in a *.D64 file) each sector consists of 256 Byte. In order to be able to deal with bad sectors the *.D64 format optionally features an additional error byte per sector. A disk that was formatted by using the standard C1541 command NEW will have 35 tracks containing a total of 683 sectors. With the use of special software it is possible to format and write disks with up to 40 tracks. While the extra tracks are not within the original specification such disks are readable with most C1541 drives (Note: Reading does not require special software.).

Currently there are four different types of *.D64 formats:
• 683 Sectors (= 35 Tracks) without error info
   (Total Size: 174848 Byte = 170.75 KByte)
• 683 Sectors (= 35 Tracks) with error info
   (Total Size: 175531 Byte = 171.42 KByte)
• 768 Sectors (= 40 Tracks) without error info
   (Total Size: 196608 Byte = 192 KByte)
• 768 Sectors (= 40 Tracks) with error info
   (Total Size: 197376 Byte = 192.75 KByte)

Note that tracks at the outside edge of the disk contain more sectors than those near to the disks center (Zone Bit Recording). The number of sectors on each track is:
      Tracks  1..17 - 21 Sectors
      Tracks 18..24 - 19 Sectors
      Tracks 25..30 - 18 Sectors
      Tracks 31..35 - 17 Sectors
      Tracks 36..40 - 17 Sectors (non standard!)

The Block Allocation Map (BAM) is stored on track 18 - sector 0; the directory starts at track 18 - sector 1.

For storage in a *.D64 image the sectors are arranged in the followingway:
  Track  1 - Sector  0: Offset   0 * 256
  Track  1 - Sector  1: Offset   1 * 256
                .....
  Track  1 - Sector 20: Offset  20 * 256
  Track  2 - Sector  0: Offset  21 * 256
  Track  2 - Sector  1: Offset  22 * 256
                .....
  Track  2 - Sector 20: Offset  41 * 256
  Track  3 - Sector  0: Offset  42 * 256
                .....
  Track 18 - Sector  0: Offset 357 * 256
  Track 18 - Sector  1: Offset 358 * 256
                .....
  Track 35 - Sector  0: Offset 666 * 256
  Track 35 - Sector  1: Offset 667 * 256
                .....
  Track 35 - Sector 16: Offset 682 * 256

Note that tracks are counted starting with track 1, while sector numbers start with 0.

typedef struct
 
  Byte DiskImage[SectorCnt][256];
  Byte ErrorInfo[SectorCnt]; /* Optional */
  D64File;

DiskImage - 256 Bytes per Sector in
ErrorInfo - 1 Byte per Sector
Note that the error information for all sectors is concentrated  at the end of the file. There is no interweaving of disk image data and error information on a per sector basis.

C.5 ZipCode File Format (1!*, 2!*, etc.)

This is a compressed version of a D64 file that is frequently found on C64 sites that have already been around for a long time (and have served C64 owners before emulation became common). A single D64 file is split into 4 (35 tracks) or 5 (40 tracks) segments. Each part is then packed using the simple (and poorly compressing) Run Length Encoding compression method.

The primary reason for the existance of this file format is that every segment of such a ZipCode File is less than 44 KByte in size, and can thus easily be handled by a real C64 and its 1541 disk drive (say after a transmittion via modem from a BBS). Furthermore packing and unpacking are very fast on a C64.

Modern computers, like a Power Macintosh, that are used to run C64 emulators have no problem handling files 171KByte in size (like D64 files), so they do not benefit from splitting one 'large' file in several smaller. In fact, due to the large allocations units of modern hard disks and CD-ROMs the space wasted by 4 half filled last blocks of the 4 files is likely to exceeds the savings from compression for reasonable well filled floppy disks. Furthermore modern compression formats like Zip, GZ, LHA or SIT offer much besser compression that the RLE used in the ZipCode. 

While ZipCode is not the best choice of a file format for an emulator it is still a good format on a real C64 and many sites offer their files in ZipCode format to support the faithful C64-addicts that stick to the real hardware.

Note that there are also two other Zip formats for the C64, that are not supported by Power64. They use the filename-prefixes 1!!, 2!!, 3!!…  and A!, B!, C!…  respectivly. They are described in the File Format collection of Peter Schepers the author of 64COPY (schepers@dcs1.uwaterloo.ca), but I have never seen files in either format anywhere on the web.

The contents of the floppy disk is devided onto the 4 or 5 segments accoring to the following table:
   FileName  Track Range  Block Count
   --------  -----------  -----------
   1!xxxxxx     1 - 8     168 Sectors
   2!xxxxxx     9 - 16    168 Sectors
   3!xxxxxx    17 - 25    172 Sectors
   4!xxxxxx    26 - 35    175 Sectors
   5!xxxxxx    36 - 40     85 Sectors (only for 40 track disk images)

All segments have a similar structure based on compressed disk sectors. Note that the first segments header is slightly different from the rest.

typedef struct
 
  Byte LoadAddr[2];     /* Const. $03FE =  0xFE, 0x03; */
  Byte FloppyID[2];
  ZipSector Image[168];
  ZipSegment_1!;

typedef struct
 
  Byte LoadAddr[2];     /* Const. $0400 =  0x00, 0x04; */
  ZipSector Image[...];
  ZipSegment_n!;       /* n = 2, 3, 4, 5 */

Compression takes place on a sector per sector basis. For each sector there is the following structure:

typedef struct
 
  Byte Track;     /* Track Number and Compression Mode */
  Byte Sector;    /* Sector Number */
  union 
     Byte NoCompression[256];  Mode00;
     Byte SingleByte;  Mode01;
     Byte Length;
      Byte RepeatChar;
      Byte RLEData[Length];  Mode10;
    Data;
  ZipSector;

Track gives the track number of the compressed sector in Bit 5 to Bit 0. Bits 7&6 give the compression mode.
Sector gives the sector number of the compressed sector.
The meaning of Data depends on the compression mode:

Compression Modes:
00 - No compression; The sector is stored in full. The 256 Data Bytes give the sectors contents.
01 - The entire sector is filled with a single Byte. A single Data Byte will be repeated 256 times to fill the sector.
10 - Run Length Encoded Sector - Length gives the number of Bytes of compressed RLEData. RepeatChar is a Byte that does not appear in the original sector and that is now used as a marker for runlength encoded pieces of data. RLEData  different from RepeatChar is just plain, uncompressed data, that can be directly copied. After RepeatChar is encountered, the next Byte of RLEData gives the Length of the Run and 
a third Byte denotes the Byte to be repeated.
11 -Unused   

The sectors of a track are NOT stored in a linear fashion (1, 2, 3, 4…). To make packing and unpacking much faster on a real 1541 interleaving is used. The proper sequence of sectors depends on the number of sectors in a track and can be seen below.
Track  1-17:   0  11  1  12  2  13  3  14  4  15  5  16  6  17  7  18  8  19  9  20  10
Track 18-24:  0  10  1  11  2  12  3  13  4  14  5  15  6  16  7  17  8  18  9
Track 25-30:  0    9  1  10  2  11  3  12  4  13  5  14  6  15  7  16  8  17
Track 31-40:  0    9  1  10  2  11  3  12  4  13  5  14  6  15  7  16  8

Example for the first few sectors of the first segment (1!*):
$0000: 0xFE, 0x03,       /* Load Addr. Const $03FE */
$0002: 0x36, 0x34,       /* Floppy ID */
$0004: 0x41, 0x00, 0x00, /* Track 1, Sector  0 filled with 0x00 */
$0007: 0x41, 0x0B, 0x00, /* Track 1, Sector 11 filled with 0x00 */
$000B: 0x41, 0x01, 0x00, /* Track 1, Sector  1 filled with 0x00 */
$0007: 0x01, 0x0C,       /* Track 1, Sector 12 uncompressed Data */
... 256 Byte Data ...
$0109: 0x81, 0x02,       /* Track 1 ,Sector 2 RLE Encoded */
       0x12, 0xEA,       /* 0x12(18) Byte, RepeatChar: 0xEA */
       0x45, 0x22, 0x34, 0x08,/* Plain Data */
       0xEA, 0xD0, 0x77,      /* 0x77 repeated 0xD0 times */
       0x12, 0xFF, 0x00, 0x00, 0x32, 0x11 /* Plain Data */
       0xEA, 0x24, 0x55,      /* 0x55 repeated 0x24 times */
       0xEE, 0x98,            /* Plain Data */
$011F: 0x41, 0x0D, 0x11  /* Track 1, Sector 13 filled with 0x11 */
...

C.6 *.Lynx File Format 

The Lynx (or LNX or Ultimate Lynx) file format was developed by Will Corley for use on the C64(!). It is designed around blocks of 254 Bytes.This corresponds to the 256 Bytes of a 1541 disk sector minus the 2 Bytes that contain the next track/sector information and makes it easy to copy files between 1541 disks and Lynx files.
Unfortunately the Lynx header is written in a format that is somewhat harder to handle than that of a T64 or D64 file, and thus the format has received little attention by emulator  authors.

typedef struct
 
  Byte Directory[DirSectorCnt * 254];
  Byte Data[DataSectorCnt * 254];
  LynxFile;

The Directory starts out with a small BASIC program which, when loaded and run, displays the message "Use LYNX to dissolve this file". The  actual message and size of the program can change. Usually, its 94 bytes long, from $0000 to $005D. Note that some emulators depend on the exact size of 94 Bytes and also require that the text 'LYNX' can be found at offset $003C to $003F. 

$0000: 0x01, 0x08, 0x5B, 0x08, 0x0A, 0x00, 0x97, 0x35, /* .......5 */
$0008: 0x33, 0x32, 0x38, 0x30, 0x2C, 0x30, 0x3A, 0x97, /* 3280,0:. */
$0010: 0x35, 0x33, 0x32, 0x38, 0x31, 0x2C, 0x30, 0x3A, /* 53281,0: */
$0018: 0x97, 0x36, 0x34, 0x36, 0x2C, 0xC2, 0x28, 0x31, /* .646,.(1 */
$0020: 0x36, 0x32, 0x29, 0x3A, 0x99, 0x22, 0x93, 0x11, /* 62):.".. */
$0028: 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, /* ......." */
$0030: 0x3A, 0x99, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, /* .."      */
$0038: 0x55, 0x53, 0x45, 0x20, 0x4C, 0x59, 0x4E, 0x58, /* USE LYNX */
$0040: 0x20, 0x54, 0x4F, 0x20, 0x44, 0x49, 0x53, 0x53, /*  TO DISS */
$0048: 0x4F, 0x4C, 0x56, 0x45, 0x20, 0x54, 0x48, 0x49, /* OLVE THI */
$0050: 0x53, 0x20, 0x46, 0x49, 0x4C, 0x45, 0x22, 0x3A, /* S FILE": */
$0058: 0x89, 0x31, 0x30, 0x00, 0x00, 0x00,             /* .10...   */

This is equivalent to the following BASIC program: 
   10 POKE53280,0:POKE53281,0:POKE646,PEEK(162):
        PRINT"<CLS><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN>":
        PRINT"     USE LYNX TO DISSOLVE THIS FILE":
        GOTO10

Following this is a <CR> and the number of blocks of the directory in ASCII with spaces on both sides. For a directory with just 1 block that would be:
$005E:  0x0D, 0x20, 0x31, 0x20, 0x20                    /* . 1     */

After this there is the "signature" of the archive, an CBM lower case (ASCII for the most part) text ending in <CR> that describes the Lynx archive. Normally this signature will contain the string 'LYNX'.
Power64 uses:
$0063: 0x2A, 0x4C, 0x59, 0x4E, 0x58, 0x20, 0x41, 0x52, /* *LYNX AR */
$006B: 0x43, 0x48, 0x49, 0x56, 0x45, 0x20, 0x42, 0x59, /* CHIVE BY */
$0073: 0x20, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x36, 0x34  /*  POWER64 */
$007B: 0x0D

This is followed by the number of files in the Lynx archive, as before this is given as a ASCII number surrounded by spaces and delimited by <CR>.
For a directory with 3 files that would be:
$007C: 0x20, 0x33, 0x20, 0x0D                          /*  3 .     */

Following these headers is the actual directory. Each file is described by its filename (in PETASCII, typically padded to 16 characters by shifted-spaces), followed by the size of the file (plus 2 bytes for the address) in blocks of 254 bytes, the file type (P, S, R, U), the size in bytes of the last block. If the file type is Relative, this entry is the Record size, and the next entry is the last block size. If the file type is not Relative, the next entry will be the next filename. Relative files are not supported by Power64. Every entry is terminated by a <CR>, numbers are surrounded by spaces. For example this could be:

File 1: "Block Out", PRG, 2+2519 Byte (2521 = (10-1) * 254 + 235)
$0080: 0x42, 0x4C, 0x4F, 0x43, 0x4B, 0x20, 0x4F, 0x55, /* BLOCK OU */
$0088: 0x54, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* T††††††† */
$0090: 0x0D, 0x20, 0x31, 0x30, 0x20, 0x0D, 0x50, 0x0D, /* . 10 .P. */
$0098: 0x20, 0x32, 0x33, 0x35, 0x20, 0x0D,             /*  235 .   */

File 2: "Serpentine", PRG, 2+8703 Byte (8705 = (35-1) * 254 + 69)
$009E: 0x53, 0x45, 0x42, 0x4C, 0x45, 0x4E, 0x54, 0x49, /* SERPENTI */
$00A6: 0x4E, 0x45, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* NE†††††† */
$00AE: 0x0D, 0x20, 0x33, 0x35, 0x20, 0x0D, 0x50, 0x0D, /* . 35 .P. */
$00B6: 0x20, 0x36, 0x39, 0x20, 0x0D,                   /*  69 .    */

File 3: "Quardomania", PRG, 2+7056 Byte (7058 = (28-1) * 254 + 200)
$00BB: 0x51, 0x55, 0x41, 0x44, 0x52, 0x4F, 0x4D, 0x41, /* QUADROMA */
$00C3: 0x4E, 0x49, 0x41, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* NIA††††† */
$00CB: 0x0D, 0x20, 0x32, 0x38, 0x20, 0x0D, 0x50, 0x0D, /* . 28 .P. */
$00D3: 0x20, 0x32, 0x30, 0x30, 0x20, 0x0D,             /*  200 .   */

The actual files follow the directory. Each file consists of 2 bytes load address plus the actual data.

Note that the directory and every files size is a multiple of 254 bytes. If the actual information requires less space, then there is a wasted gap, that is filled with 0x00 in Power64 (other implementations vary).

In the above example the directory consists of just one block, and thus Block Out starts at offset 1 * 254. The second file (Serpentine) starts at offset (1+10) * 254 and Quadromania begins at (1+10+35) * 254.

$00FE: 0x01, 0x08, 0x26, 0x08, 0xC1, 0x07, 0x9E, 0x32, /* ..&.i..2 */
$0106: 0x30, 0x38, 0x38, 0x3A, 0x12, 0x42, 0x4C, 0x4F, /* 088:.BLO */
$011E: 0x43, 0x4B, 0xAB, 0x4F, 0x55, 0x54, ...         /* CK-OUT.. */
... /* (1+10) * 254 = $0AEA */
$0AEA: 0x01, 0x08, 0x26, 0x08, 0xC1, 0x07, 0x9E, 0x32, /* ..&.¡..2 */
$0AF2: 0x30, 0x38, 0x38, 0x3A, 0x12, 0x53, 0x45, 0x52, /* 088:.SER */ 
$0AFA: 0x50, 0x45, 0x4E, 0x54, 0x49, 0x4E, 0x45,...    /* PENTINE  */
...

C.7 *.T64 Tape Image Format 

The *.T64 file format is a very well designed (so in former times poorly documented) format, developed by Miha Peternel for C64S. Like a disk image file it is able to contain many logical files; a great help when keeping lots of files organized. On the other hand there is very little organizational overhead, both in terms of lost space on the host file system and in terms loading time for the emulator. As a *.T64 file contains a magic header it is furthermore possible for the emulator to prevent invalid files from being used.

typedef struct
 
  TapeHeader Header;          /*  $0000  -  $003F  */
  TapeEntry  Entry[MaxFiles]; /*  $0040  - ($03FF) */
  Byte       Data[n];         /* ($0400) -   ...   */
  T64File;

Header - General information; containing a magic string to identify a *.T64 file, the number of files on the tape etc... (see below for details)
Entry  - Tape Directory; A list of all files stored on the tape. The value MaxFiles is defined in Header, but is usually 30. (see below for details)
Data   - The actual data for all the files.

typedef struct
 
  Byte TapeDescr[32]; /* $00 - $1F */
  Byte Version[2];    /* $20 - $21 */
  Byte MaxFiles[2];   /* $22 - $23 */
  Byte CurrFiles[2];  /* $24 - $25 */
  Byte Reserved[2];   /* $26 - $27 */
  Byte UserDescr[24]; /* $28 - $3F */
  TapeHeader;

TapeDescr - Magic string: "C64 tape image file"; padded with $00
            Note this magic string need not be verbatim. Search for
            substrings "C64" and "tape" to identify a *.T64 file.
Version   - Tape Version 1.0 = $00, $01
MaxFiles  - Number of places in the tape directory. There are some
            some emulators that rely on fact that there are exactly
            30 places in the directory. Thus all *.T64 files created
            by Power64 will supply this value. = $1E, $00
CurrFiles - Number of Files currently stored on the tape. This number
            can never exceed MaxFiles. Note that some emulators can only
            read the first file stored in a *.T64 image.
Reserved  - Must be $00.
UsedDescr - User Description of the Tape. (CBM character set)

typedef struct
 
  Byte EntryUsed;     /* $00 */
  Byte FileType;      /* $01 */
  Byte StartAddr[2];  /* $02 - $03 */
  Byte EndAddr[2];    /* $04 - $05 */
  Byte ReservedA[2];  /* $06 - $07 */
  Byte TapePos[4];    /* $08 - $0B */
  Byte ReservedB[4];  /* $0C - $0F */
  Byte FileName[16];  /* $10 - $1F */
  TapeEntry;

EntryUsed - Is this entry in the *.T64 directory used? (0..No, 1..Yes)
FileType  - There is very little documentation on this field.
            Power64 adopts the notion of Star Commander that corresponds
            to the file type identifiers used on floppy disk:
            $82..Relocateable Prg, $81..Data File
            Earlier versions of Power64 implemented the following meaning:
            0..Relocateable Prg., 1..Force Load Prg., 2..Data File
            To prevent error messages all other values are also treated
            a relocateable programs.
StartAddr - Start of the destination range in C64 memory. (Low/High)
EndAddr   - End of the destination range in C64 memory. (Low/High)
ReservedA - Must be $00
TapePos   - Offset from the start of the tape file to the logical
            files data
ReservedB - Must be $00
FileName  - filename (CBM character set)

There was very little official documentation on the *.T64 file format. What is known was generally learned by examining existing *.T64 files. Sometimes that was not done with sufficient scrutiny. Thus there are many *.T64 files on the internet that do not follow the described format. It is, for example quite common, that the difference between EndAddr and StartAddr of a file is not consistent with the file size of the *.T64 file. Power64 attempts to automatically fix such inconsistencies.

As stated above there are some emulators that rely on the fact that there are always exactly 30 directory entries, while at the same time they will always load the first file. Please keep this in mind when making Commodore 64 files available to a general public that might use a wide range of emulators.

All multi-byte values are stored in the Little-Endian Format (Low/High) that is generally used by the C64.

C.8 RAM Snapshot File

The format for RAM Snapshots was adapted from a suggestion made by Jouko Valta for the Vice-Emulator on Unix systems. The original description, that explains the meaning of the individual header fields in more detail, can be found at: http://stekt.oulu.fi:/~jopi/x64/RAM-format

typedef struct
 
  Byte MagicCode[6];     /* $00 - $05 */
  Byte ReservedA[2];     /* $06 - $07 */
  Byte Version[2];       /* $08 - $09 */
  Byte EmulatorID;       /* $0A */
  Byte SnapType;         /* $0B */
  Byte RAMStart[2];      /* $0C - $0D */
  Byte RAMSizeKB[2];     /* $0E - $0F */
  Byte REUSizeKB[2];     /* $10 - $11 */
  Byte ReservedB[14];    /* $12 - $1F */
  Byte Config[6];        /* $20 - $25 */
  Byte IORAM;            /* $26 */
  Byte CPURestore;       /* $27 */
  Byte PCHigh, PCLow;    /* $28 - $29 */
  Byte RegA, RegX, RegY; /* $2A - 2C */
  Byte RegFlags, RegSP;  /* $2D - $2E */
  Byte IOPort;           /* $2F */
  Byte Z80_CPU[16];      /* $30 - $3F */
  Byte REC[16];          /* $40 - $4F */
  Byte OSVersion;        /* $50 */
  Byte OSPatch;          /* $51 */
  Byte PatchID[2];       /* $52 - $53 */
  Byte ReservedC[44];    /* $54 - $7F */
  Byte Custom[128];      /* $80 - $FF */
  Byte RAM[65536];
  Byte IOArea[4096];
  Byte REU[REUSize];
  FloppyState Floppy1541[FloppyCnt];
  Snapshot;

Note: All multi-byte values are stored in Big-Endian Format (High/Low) rather than in the Little-Endian Format (Low/High) used by the C64.

MagicCode  - Magic Constant 'CBM'64 = $43, $42, $4D, $64, $00, $00
ReservedA   - Should be $00, $00
Version        - Major and Minor Version Number 1.0 = $01, $00
EmulatorID  - Power64 uses '/' = $2F
SnapType    - RAM Snapshot = $00
RAMStart    - Offset to the RAM Image in the File. The header currently uses 256 Bytes. Thus RAMStart = $01, $00.
RAMSizeKB - Size of the RAM Image in KByte (not Byte!) = $00, $40
REUSizeKB - Size of the RAM Expansion Unit in KByte
ReservedB   - Should be all $00
Config         - Reserved for configuration bit masks - Currently all $00
IORAM       - Bit 2/1: VDC RAM available (00..No,   01..16KB, 10..64KB, 11..32KB)
                      Bit 0:   I/O data available (0..No, 1..Yes)
                      Value used by Power64: $01
CPURestore - CPU(s) available: (Bit 0: M6502, Bit 1: Z80, Bit 2: REC) = $01
PCHigh, PCLow - M6502 Program Counter
RegA, RegX, RegY, RegFlags, RegSP - M6502 Registers
IOPort          - 6502 CPU I/O latch
Z80_CPU    - Reserved for internal state of Z80 (not used)
REC            - Reserved for internal state of REC (not used)
OSVersion  - C64 Kernal ID byte (not used)
OSPatch      - Kernal patch ID for extensions (fastloaders etc.) (not used)
PatchID       - Patch version information  (not used)
ReservedC  - Kernal version information (not used)
Custom       - Information on peripherals, external devices etc. (not used)
RAM           - Contents of the RAM
IOArea        - Contents of the I/O-Area (VIC, SID, ColorRAM, CIA1/2, REU)
             Every device is stored only once, even so several shadows appear in the C64 address space. The gaps are filled with Power64 internal state information and lots of $00.
REU            - The Contents of the REU RAM, if a RAM Expansion is present.
                      0, 128, 256 or 512 KByte - as specified by REUSizeKB.
Floppy1541 - The current state of each attached, completly emulated Floppy 1541.
     typedef struct
   
    Byte MagicCode[6];     /* $00 - $05 ('CBM',$15, $41, $00) */
    Byte ReservedA[2];     /* $06 - $07 */
    Byte Version[2];       /* $08 - $09 */
    Byte EmulatorID;       /* $0A */
    Byte SnapType;         /* $0B */
    Byte RAMStart[2];      /* $0C - $0D */
    Byte RAMSizeKB[2];     /* $0E - $0F */
    Byte REUSizeKB[2];     /* $10 - $11 */
    Byte ReservedB[14];    /* $12 - $1F */
    Byte Config[6];        /* $20 - $25 */
    Byte IORAM;            /* $26 */
    Byte CPURestore;       /* $27 */
    Byte PCHigh, PCLow;    /* $28 - $29 */
    Byte RegA, RegX, RegY; /* $2A - 2C */
    Byte RegFlags, RegSP;  /* $2D - $2E */
    Byte ReservedC[81];    /* $2F - $7F */
    Byte Custom[128];      /* $80 - $FF */
    Byte RAM[2048];
    Byte IOArea[512];
  FloppyState;